<?php
/**
 * Defines Kodform class
 *
 *
 * @copyright 	Copyright (c) 2006 Boris Tomic (kodmasin.net)
 * @category 	Kodform
 * @package 	Kodform
 * @author 		Boris Tomic
 * @license 	http://boris.kodmasin.net/kodform4:license The BSD License
 *
 */

require_once 'Kodform/Element.php';
require_once 'Kodform/Element/Submit.php';
require_once 'Kodform/Element/Reset.php';
/**
 * class which implements form element
 */
class Kodform{
	/**
	 * @var array Holds form elements
	 */
	protected $elements;
	/**
	 * @var array form errors
	 * 
	 * This is array in form
	 * <code>
	 * 
	 * </code>
	 */
	private $errors;
	/**
	 * @var string of form used for name attribute
	 */
	private $id;
	/**
	 * @var string holds action value. That is action form
	 * tag attribute. 
	 */
	protected $action;
	/**
	 * @var integer holds method information
	 * 
	 * it holds index of method defined in $methods member
	 * 
	 * @see $methods
	 */
	private $method;
	/**
	 * @var array submit form element added to this form
	 */
	private $submits;
	/**
	 *
	 * @var array of reset buttons added to this form
	 */
	private $resets;
	/**
	 * @var mixed
	 */
	private $valid;
	/**
	 * @var array available methods for form tag attribute method
	 * @static
	 * 
	 */
	public $methods = array("post", "get");
	/**
	 * Creates new form element
	 * 
	 * @param string $id id (name) of form
	 * @param integer $method defines method (0 => post, 1 => get)
	 * @param string $action this is url where form should post infact
	 * 	this is action attribute
	 */
	public function __construct($id, $method = 0, $action = null){
		$this->elements = array();
		$this->errors = array();
		$this->submits = array();
		$this->resets = array();
		$this->id = $id;
		$this->method = $method;
		$this->action = $action;
		$this->valid = null;
	}
	/**
	 * check if this form is valid
	 * 
	 * It check if form has already been validated if yes
	 * it returns that time validation result. If form has
	 * not been validated validate form and reutrn result
	 * 
	 * @return boolean true if valid false iuf not 
	 */
	public function isValid(){
		if($this->valid !== null)
		return $this->valid;
		$this->valid = true;
		foreach($this->elements as &$element){
			if(!$element->validate()){
				$this->valid = false;
				$this->errors[$element->getDisplayName()] = $element->getErrors();
			}
		}
		return $this->valid;
	}
	/**
	 * This function is itended to be used if form is 
	 * extended by another class
	 * 
	 * I Am not shure will this be implemented so for
	 * now this is just place holder.
	 */
	public function process(){
		return ;
	}
	/**
	 * Adds new element to form
	 * 
	 * @param Kod_Form_Element $element this is new element to be
	 * added to this form
	 * @return Kod_Form_element $element returns same element which is added
	 */
	public function &addElement(Kodform_Element &$element){
		if($element instanceof Kodform_Element_Submit)
		$this->submits[$element->getId()] =& $element;
		else if($element instanceof Kodform_Element_Reset)
		$this->resets[$element->getId()] =& $element;
		else{
			$this->elements[$element->getId()] =& $element;
		}
		$element->setForm($this);
		return $element;
	}
	/**
	 * Returns all errors of this form.
	 * 
	 * It should be called after isValid method othervise it
	 * will return empty array which toes not neceseary mean
	 * that values in form are valid
	 * 
	 * @return array in form:
	 * <code>
	 * array("element1_name" => array("Error1", "Error2"),
	 * 		"element2_name" => array("Error3", "Error"));
	 * </code>
	 */
	public function getErrors(){
		return $this->errors;
	}
	/**
	 * sets charachter which will be used for marking
	 * required fields.
	 */
	public function setRequiredMark($string){
		$this->requiredMark = $string;
	}
	/**
	 * checks if this form has required elements
	 * 
	 * @return boolean true if form has required elements
	 * and false if not
	 */
	public function hasRequiredElements(){
		$yes = false;
		foreach($this->elements as &$element)
		if($element->isRequired())
		$yes = true;
		return $yes;
	}
	/**
	 * checks if this form is submited
	 * 
	 * @return boolean returns true if form is submited
	 * and fals eif it is not
	 */
	public function isSubmited(){
		foreach($this->submits as $submit){
			if($submit->isSubmited())
			return true;
		}
		return false;
	}
	/***/
	public function getSubmit($name){
		if(array_key_exists($name, $this->submits))
		return $this->submits[$name];
		throw new Exception("There is no submit under name " . $name . " in this form!.");
	}
	/**
	 * returns method which this form is using (post, get)
	 */
	public function getMethod(){
		return $this->methods[$this->method];
	}
	public function getAction(){
		return $this->action;
	}
	/**
	 * returns POST or GET array.
	 * 
	 * Depends on which method is set
	 */
	public function &getRawData(){
		if($this->method == 0)
		return $_POST;
		if($this->method == 1)
		return $_GET;
		return null;
	}
	/**
	 * returns raw (submited) data of form element
	 * 
	 * @param string $id name of form element
	 * @return mixed raw value of form element or null if 
	 * element does not exist in this form 
	 */
	public function getRaw($id){
		$data = $this->getRawData();
		if(array_key_exists($id, $data))
		return $data[$id];
		return null;
	}
	/**
	 * function for quick rendering of form (for fast prototyping) but 
	 * you can provide any template you like
	 * @param string $template path to form template
	 */
	public function display($template = null){
		if($template == null)
		$template = dirname(__FILE__) . DIRECTORY_SEPARATOR . "Kodform" . DIRECTORY_SEPARATOR . "Template" . DIRECTORY_SEPARATOR . "Default.php";
		ob_start();
		if(file_exists($template))
		require $template;
		else
		throw new Exception("Template file does not exists. Sorry can not render form.");
		return ob_get_clean();
	}
	public function clear(){
		foreach($this->elements as &$element)
		$element->clear();
		foreach($this->submits as $submit)
		$submit->clear();
		$this->valid = null;
	}
	public function &__get($name){
		if(array_key_exists($name, $this->elements))
		return $this->elements[$name];
		throw new Exception("There is no element under name " . $name . " in this form!.");
	}
	/**add error to from errors
	 * this function add error to form errors and invalidate form
	 * Use it when you have to process from data but after that you find that data is not valid
	 * fopr ex. login form where you get invalid after you check login data*/
	public function addError($name, $message){
		$this->errors[$this->id . $name] = array($message);
		$this->valid = false;
	}
	public function getId(){
		return $this->id;
	}
	public function removeElement($id){
		if(array_key_exists($id, $this->elements))
		unset($this->elements[$id]);
		if(array_key_exists($id, $this->submits))
		unset($this->submits[$id]);
		if(array_key_exists($id, $this->resets))
		unset($this->resets[$id]);
	}
}